feat(lib): capture client-attested build provenance - #454
Merged
max-parke-scale merged 15 commits intoJul 29, 2026
Conversation
Add agentex.lib.utils.build_provenance — the single producer of source identity for agent builds (git coordinates + a deterministic content hash of the build context). prepare_cloud_build_context now writes build-info.json into the staged context (populates runtime registration_metadata with no server change) and exposes provenance on CloudBuildContext so the upload can send source_* fields. Archive member order is now deterministic via a sorted enumeration shared with the hash. The hash is computed only when there is no clean commit to identify the build (dirty tree or non-git context). First of three surfaces for AGX1-418 (Phase 1, client-attested); the SGP build-record columns and the sgpctl/Gitea uploaders follow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address Greptile review on the build-provenance capture util: - Always compute working_tree_hash (drop the "skip on clean commit" path). A `git status` clean tree can still contain .gitignore'd-but-not- .dockerignore'd files the commit can't reproduce; an always-present content hash identifies the exact shipped bytes and closes that gap. - Guard the hash (_safe_working_tree_hash) so a permission error or filesystem race degrades to None instead of aborting the build — the module contract is that capture never raises into a build. - Record dirtiness as a first-class `dirty` flag (surfaced as `source_dirty` / `dirty`) rather than overloading hash-presence, matching Go's vcs.modified and Nix's dirtyRev. None outside a git work tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
Addressed both Greptile findings in cf9994d:
Also, per design discussion: dirtiness is now a first-class 🧑💻🤖 — posted via Claude Code |
Greptile (T-Rex repro) showed build-info.json was written to the archive root, which the templates' Dockerfiles don't COPY and the runtime locate_build_info_path() doesn't read — so it never reached the image and the registration_metadata sink stayed empty. Beyond the placement bug, the sink is redundant: AgentexCloudDeploy.build_id is an FK to AgentexCloudBuild, so a deployment's source provenance derives from the build record (the source_* columns this work adds, Surface C) over that join — the same Build->Deploy edge lineage already traverses. No need to denormalize provenance onto registration_metadata/DeploymentHistory (which has had no producer since its read path landed 2025-09, so its git fields have never been populated). #454 now ships only the shared capture util (agentex.lib.build_provenance) plus a deterministic build-archive ordering. Provenance is delivered via the build-record sink; the runtime sink can be revived (correctly placed) if a real consumer for deployment-history provenance ever appears. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ld-provenance-capture
#460) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ld-provenance-capture # Conflicts: # uv.lock
…ty trace (#465) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
One add/add conflict in obs_ids.py, which both branches created independently. Took next's side on both hunks: - `from typing import Dict, Tuple, Optional` — next's length-sorted order is what ruff's isort config wants here. This branch's alphabetical order was the I001 that had been failing the lint job, so resolving the conflict fixes it. - `from ddtrace.trace import tracer` — the path ddtrace exposes now, and the only form used elsewhere in the tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
danielmillerp
approved these changes
Jul 29, 2026
deepthi-rao-scale
added a commit
that referenced
this pull request
Jul 29, 2026
…pping stale source The tutorial-agent build/publish pipeline could silently republish a stale image to the moving ':latest' tag. 'agentex agents build' invoked 'docker.buildx.build' with no cache control, so a cached layer could ship source that no longer matched the checkout -- e.g. the merged 'mcp<2' pin for the 020_state_machine agent never reached ':latest', leaving integration tests pulling a months-old image and failing on the mcp 2.0.0 'McpError' rename. - add a 'cache' param to build_agent() -> passes cache=False (buildx --no-cache) through to the build - expose '--cache/--no-cache' on 'agentex agents build' (default: cache on, so local dev and immutable SHA builds stay fast) - build-and-push-tutorial-agent.yml uses --no-cache only for the ':latest' publish path; SHA-tagged validation builds keep the cache Related: build-provenance work (#454) records a working-tree hash and could later provide a more surgical cache-key-based fix; this is the immediate, guaranteed prevention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
agentex.lib.utils.build_provenance— the shared capture util for client-attested build provenance: git coordinates (repo/commit/ref/subpath), a deterministicworking_tree_hashover the build inputs (not the tarball), adirtyflag (Govcs.modified/ NixdirtyRevshape), andnormalize_remote. Capture is best-effort and never raises into a build. Also makes the build archive’s member order deterministic via a sorted enumeration shared with the hash.First of three surfaces for AGX1-418 (Phase 1, client-attested). Provenance is delivered via the build-record sink —
source_*columns onPOST /v5/builds(Surface C, scaleapi) consumed by the sgpctl + CI uploaders (Surface B, scaleapi/sgp). This PR lands the util + archive determinism whereagentex.liblives; the uploaders/columns follow.Scope notes
build-info.json/ runtime sink. An earlier revision wrotebuild-info.jsoninto the build context for theregister_agent()→registration_metadatapath. Greptile (T-Rex) correctly flagged it as dead-on-arrival (written to the archive root, which the templates’ Dockerfiles don’t COPY andlocate_build_info_path()doesn’t read). It’s also redundant:AgentexCloudDeploy.build_idis an FK toAgentexCloudBuild, so a deployment’s source provenance derives from the build record over that join — the same Build→Deploy edge lineage already traverses. Dropped; can be revived (correctly placed) if a real consumer for deployment-history provenance ever appears.Identity model
working_tree_hashis always computed (content identity);commit/ref/repoanchor it to source when in a git work tree;dirtyrecords uncommitted changes (Noneoutside git).Tests
20 provenance unit tests (clean/dirty/untracked/detached-HEAD/no-remote/non-git/monorepo-subpath, hash determinism + one-byte/added/exec-bit/symlink sensitivity, and a never-raises-on-hash-failure guard).
ruff/pyrightclean; fulllibsuite green.🧑💻🤖 — posted via Claude Code
Greptile Summary
Adds
agentex.lib.utils.build_provenance— a best-effort utility that captures git coordinates, a deterministicworking_tree_hashover the build context, and adirtyflag. The archive tarball inagent_manifest.pyis updated to use the same sortediter_context_filesenumeration, ensuring member order matches the hash.build_provenance.py: New module withBuildProvenancedataclass,capture_build_provenanceentry point,normalize_remote,iter_context_files, andworking_tree_hash. All error paths degrade gracefully via_safe_working_tree_hashand_gitexception handling.agent_manifest.py: Minimal change — replaces the inlinerglobloop inBuildContextManager.zipped()withiter_context_filesto share the deterministic sort with the hash.Confidence Score: 5/5
Safe to merge — provenance capture is fully best-effort, the tarball determinism change is a straightforward refactor, and the test coverage is comprehensive.
The two observations are minor: one method (
build_info) has no callers yet, and one field key insource_fields()may have a naming inconsistency with the server API. Neither affects the correctness of the build pipeline or the provenance data Surface B will consume. The hash-failure and git-failure paths are both properly guarded.Files Needing Attention: Confirm the
working_tree_hashcolumn name onPOST /v5/buildsagainst the Surface C spec before the Surface B uploaders land.Important Files Changed
_safe_working_tree_hashcorrectly wraps the hash path. Two minor issues:build_info()is dead code (no callers after build-info.json removal), andworking_tree_hashkey insource_fields()lacks thesource_prefix unlike all other keys.rglobloop withiter_context_files, making the archive member order deterministic and consistent with the hash computation. Change is minimal and correct.source_fields()andbuild_info()serialization methods are not directly exercised.Reviews (8): Last reviewed commit: "Merge origin/next into maxparke/agx1-418..." | Re-trigger Greptile